#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/udp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <sys/time.h>
#include <unistd.h>

#define BUFSIZE 1500

struct outdata 
{             
  int number_ser;           
  int ttl;           
  struct timeval outdata_tv; 
};

struct btree
  {
  char elem;
  btree *left;
  btree *right;
  };



char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];

int descr_soc; 
int descr_raw; 
struct sockaddr_in sasend;
struct sockaddr_in sabind;
struct sockaddr_in sarecv;
struct sockaddr_in salast;
int sport;
int dport;
int ttl;
int probe;
int max_ttl = 20;
int nprobes = 3;
int dport = 34000;
int datalen = sizeof(struct outdata);
int packet_ok(int, struct timeval *);




btree *build_tree ()
{
btree *d;
char sym;
fscanf(fp,"%c",&sym);
switch(sym)
  {
  case '(':   {

               d=new btree;
               fscanf(fp, "%c", &sym);
               d->elem=sym;
               d->left=build_tree();
               d->right=build_tree();
               fscanf(fp, "%c",&sym);
               return d;
               }
 case '0':  return NULL;
 case ',': d=build_tree();
           return d;
  }
  return NULL;
}
 
int main(int argc, char *argv[])
{
  int seq;
  int code;
  int done;
  double rtt;
  struct hostent *hp;
  struct outdata *outdata;
  struct timeval tvrecv;

  setuid(getuid());

  sport = (getpid() & 0xffff) | 0x8000;

  bzero(&sasend, sizeof(sasend));
  sasend.sin_family = AF_INET;
  sasend.sin_addr= *((struct in_addr *) hp->h_addr);

  sabind.sin_family = AF_INET;
  sabind.sin_port = htons(sport);


  seq = 0;
  done = 0;
  for (ttl = 1; ttl <= max_ttl && done == 0; ttl++) 
	{
        	setsockopt(descr_soc, SOL_IP, IP_TTL, &ttl, sizeof(int));
   		bzero(&salast, sizeof(salast));

    		printf("%2d  ", ttl);
    		fflush(stdout);

    		for (probe = 0; probe < nprobes; probe++) 
		{
      			outdata = (struct outdata *)sendbuf;
      			outdata->number_ser = ++seq;
      			outdata->ttl = ttl;
     			gettimeofday(&outdata->outdata_tv, NULL);
    
      			sasend.sin_port = htons(dport + seq);

		        if ( (code = packet_ok(seq, &tvrecv)) == -3)
 
        			printf(" No answer");
      			else 
			{
      				  if ( memcmp(&sarecv.sin_addr, &salast.sin_addr, sizeof(sarecv.sin_addr)) != 0) 
					{
 
         				 if ( (hp = gethostbyaddr(&sarecv.sin_addr, sizeof(sarecv.sin_addr), sarecv.sin_family)) != 0)
           					 printf(" %s (%s)", inet_ntoa(sarecv.sin_addr), hp->h_name);
          				else
           					 printf(" %s", inet_ntoa(sarecv.sin_addr));
          				memcpy(&salast.sin_addr, &sarecv.sin_addr, sizeof(salast.sin_addr));
       			 }

        rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0;
        printf("  %.3f ms", rtt);

        if (code == -1)
          ++done;
      }
      
      fflush(stdout);
    }
 
    printf("\n");
  }

  return 0;
}

int packet_ok(int seq, struct timeval *tv)
{
  int n;
  int len;
  int hlen1;
  int hlen2;
  struct ip *ip;
  struct ip *hip;
  struct icmp *icmp;
  struct udphdr *udp;
  fd_set fds;
  struct timeval wait;

  wait.tv_sec = 4;    
  wait.tv_usec = 0;

  for (;;) 		
	{
    	len = sizeof(sarecv);

    FD_ZERO(&fds);
    FD_SET(descr_raw, &fds);

    if (select(descr_raw+1, &fds, NULL, NULL, &wait) > 0)
      n = recvfrom(descr_raw, recvbuf, sizeof(recvbuf), 0, (struct sockaddr*)&sarecv, &len);
    else if (!FD_ISSET(descr_raw, &fds))
      return (-3);

    gettimeofday(tv, NULL);

    ip = (struct ip *) recvbuf;	
    hlen1 = ip->ip_hl << 2;     
	
    icmp = (struct icmp *) (recvbuf + hlen1); 
    
    hip = (struct ip *) (recvbuf + hlen1 + 8);
   
    hlen2 = hip->ip_hl << 2;
    
    udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2);
	
    if (icmp->icmp_type == ICMP_TIMXCEED &&
	icmp->icmp_code == ICMP_TIMXCEED_INTRANS) {
      if (hip->ip_p == IPPROTO_UDP &&
          udp->source == htons(sport) &&
          udp->dest == htons(dport + seq))
        return (-2);
    } 
    
    if (icmp->icmp_type == ICMP_UNREACH) {
      if (hip->ip_p == IPPROTO_UDP &&
        udp->source == htons(sport) &&
        udp->dest == htons(dport + seq)) {		
	if (icmp->icmp_code == ICMP_UNREACH_PORT)
          return (-1);
      }      
    }
  }	
}
